#pragma once
#include <filesystem>
#include <string>
#include <google/protobuf/map.h>
#include <Common/Util.hpp>
#include <Common/sqliteHelper.hpp>
#include <Common/FileHelper.hpp>
#include <memory>
#include <unordered_map>
namespace MY_MQ
{

    class QueueData
    {
    public:
        using self_ptr = std::shared_ptr<QueueData>;
        std::string _name;
        bool _durable;    // 长久化标志
        bool _exclusive;  // 是否改被独有的队列标志
        bool _autoDelete; // 是否自动删除
        google::protobuf::Map<std::string, std::string> _args;
        void SetArgs(const std::string &Json_string_args)
        {
            Serialization_Util::SetArgs(_args, Json_string_args);
        }
        // 根据传入的参数args，返回序列化后的_args
        std::string GetArgs(const std::string &args)
        {
            // 调用Serialization_Util类的serializeMap方法，将_args序列化
            return Serialization_Util::serializeMap(_args);
        }
        QueueData() {};
        QueueData(const std::string &name, bool durable, bool exclusive, bool autoDelete, const std::string &args)
        {
            _name = name;
            _durable = durable;
            _exclusive = exclusive;
            _autoDelete = autoDelete;
            SetArgs(args);
        }
    };

    // 下面存储了建表信息
    const std::string QueueSqlite_table_struct = "./config/QueueSqlite_table.string";
    // 做磁盘上的持久化等该类与数据库交互
    class QueueDataBuilder
    {
    public:
        QueueDataBuilder(const std::string &db) : _sqlite_db(db)
        {
            std::filesystem::create_directories(std::filesystem::path(db).parent_path());
            _sqlite_db.open(db);
            _db_tableName = std::filesystem::path(QueueSqlite_table_struct).stem().string();
            CreateTable();
        };

        //  CREATE TABLE IF NOT EXISTS QueueSqlite_table(
        //      name varchar(32) primary key,
        //      durable int not null,
        //      exclusive int not null,
        //      auto_delete int not null,
        //      arguments text
        // )
        void CreateTable()
        {
            std::string createTableString;
            Filehelper(QueueSqlite_table_struct).read_as_string(createTableString);
            if (false == _sqlite_db.exec(createTableString))
            {
                loge("create table failed");
                abort();
            }
        }
        void removeTable()
        {
            if (false == _sqlite_db.exec("drop table " + _db_tableName))
            {

                loge("drop table failed");
            }
        }
        bool insert(QueueData::self_ptr QueueData_ptr)
        {
            std::string insert_sql = "insert into " + _db_tableName + "(name,durable,exclusive,auto_delete,arguments) values('" + QueueData_ptr->_name + "'," + std::to_string(QueueData_ptr->_durable) + "," + std::to_string(QueueData_ptr->_exclusive) + "," + std::to_string(QueueData_ptr->_autoDelete) + ",'" + QueueData_ptr->GetArgs("") + "')";
            return _sqlite_db.exec(insert_sql);
        }
        void remove(QueueData::self_ptr QueueData_ptr)
        {
            std::string remove_sql = "delete from " + _db_tableName + " where name='" + QueueData_ptr->_name + "'";
            if (false == _sqlite_db.exec(remove_sql))
            {
                loge("remove table failed");
            }
        }
        std::unordered_map<std::string, QueueData::self_ptr> recovery()
        {
            std::unordered_map<std::string, QueueData::self_ptr> res;
            std::string select_sql = "select * from " + _db_tableName;
            if (false == _sqlite_db.exec(select_sql, sql_callback, &res))
            {
                loge("select table failed");
                abort();
            }
            return res;
        }

    private:
        static int sql_callback(void *data, int argc, char **argv, char **azColName)
        {
            std::unordered_map<std::string, QueueData::self_ptr> *map_ptr = (std::unordered_map<std::string, QueueData::self_ptr> *)data;
            auto QueueData_ptr = std::make_shared<QueueData>(argv[0], std::stoi(argv[1]), std::stoi(argv[2]), std::stoi(argv[3]), argv[4] ? argv[4] : "");
            (*map_ptr)[std::string(argv[0])] = QueueData_ptr;
            return 0;
        }
        sqliteHelper _sqlite_db;
        std::string _db_tableName;
    };

    // 对外提供管理调用的接口
    // 提供创建，删除，获取指定队列，获取所有队列，判断是否存在，获取队列数量，清空队列数据
    class QueueManager
    {
        std::mutex _mutex;
        std::unordered_map<std::string, QueueData::self_ptr> _QueueDataMap;
        QueueDataBuilder _QueueDataBuilder;

    public:
        using QueueMap = std::unordered_map<std::string, QueueData::self_ptr>;
        using self_ptr = std::shared_ptr<QueueManager>;
        QueueManager(const std::string &db) : _QueueDataBuilder(db)
        {
            _QueueDataMap = _QueueDataBuilder.recovery();
        }
        bool DeclareQueue(std::string name, bool durable, bool exclusive, bool autoDelete, std::string arguments = "")
        {

            std::lock_guard<std::mutex> lock(_mutex);
            if (_QueueDataMap.find(name) != _QueueDataMap.end())
            {
                return true; // 如果存在也认为成功
            }
            auto QueueData_ptr = std::make_shared<QueueData>(name, durable, exclusive, autoDelete, arguments);
            _QueueDataMap[name] = QueueData_ptr;
            return _QueueDataBuilder.insert(QueueData_ptr);
        }
        void DeleteQueue(const std::string &name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto ptr = _QueueDataMap.find(name);
            if (ptr == _QueueDataMap.end())
            {
                return;
            }
            if (ptr->second->_durable == false)
            {
                _QueueDataBuilder.remove(ptr->second);
            }
            _QueueDataMap.erase(ptr);
        }
        QueueData::self_ptr SelectQueueData(const std::string &name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto ptr = _QueueDataMap.find(name);
            if (ptr == _QueueDataMap.end())
            {
                return nullptr;
            }
            return ptr->second;
        }
        std::unordered_map<std::string, QueueData::self_ptr> GetAllQueueData()
        {
            std::lock_guard<std::mutex> lock(_mutex);
            return _QueueDataMap;
        }
        bool IsQueueExist(const std::string &name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            return _QueueDataMap.find(name) != _QueueDataMap.end();
        }
        int GetQueueCount()
        {
            std::lock_guard<std::mutex> lock(_mutex);
            return _QueueDataMap.size();
        }
        bool exist(const std::string &name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            return _QueueDataMap.find(name) != _QueueDataMap.end();
        }
        void clearTmp()
        {
            std::lock_guard<std::mutex> lock(_mutex);
            for (auto &[name, ptr] : _QueueDataMap)
            {
                if (ptr->_durable == false)
                {
                    _QueueDataBuilder.remove(ptr);
                }
            }
            _QueueDataMap.clear();
        }
        void clear()
        {
            std::lock_guard<std::mutex> lock(_mutex);
            _QueueDataBuilder.removeTable();
            _QueueDataMap.clear();
        }
        ~QueueManager()
        {
            clear();
        }
    };
};
